package Utils;

import Bean.MindMapNode;

import java.util.ArrayList;

public class LayoutUtils {

    private static MindMapNode currentLeaf = null;

    private static int maxDeep = 1;

    private static int model;

    private static int[] dp;

    private static double xOfCenter;

    private static ArrayList<ArrayList<MindMapNode>> dpNodes = new ArrayList<>();

    public static void layoutOfAuto(MindMapNode center){
      getHigh(center);
      int halfIndex = center.getHigh()/2;
      dp = new int[halfIndex+1];
      initdpNodes(halfIndex+1);
      for(MindMapNode n : center.getChildNode()){
          int sum = (n.getHigh()==0?1:n.getHigh());
          for(int i = halfIndex;i>0;i--){
              int j = i;
              while(j>=sum) {
                      if(getClosestValue(dp[j - sum]+sum, dp[j],j)){
                          dpNodes.get(j).clear();
                          dpNodes.get(j).add(n);
                          dpNodes.get(j).addAll(dpNodes.get(j-sum));
                      }
                  j = j-sum;
              }
          }
      }
      setStatusOfdpNodes();
      center.setLeft(true);
      reOrder(center);
      layoutOfAutoLeft(center);
      center.setLeft(false);
      xOfCenter = center.getLayoutX();
      layoutOfAutoRight(center);
      dp = null;
      dpNodes = new ArrayList<>();
  }

  public static void layoutOfRight(MindMapNode center){
        center.setDeep(1);
        getDeep(center);
        reOrder(center);
        searchLeafOfRightLayout(center);
        currentLeaf = null;
        maxDeep = 1;
  }

  private static void layoutOfAutoRight(MindMapNode center){
        center.setDeep(1);
        getDeepOfAutoRight(center);
        searchLeafOfRightAutoLayout(center);
        currentLeaf = null;
        maxDeep = 1;
        xOfCenter = 0;
  }

  public static void layoutOfLeft(MindMapNode center){
        center.setDeep(1);
        getDeep(center);
        model = maxDeep;
        reOrder(center);
        searchLeafOfLeftLayout(center);
        currentLeaf = null;
        maxDeep = 1;
        model = 0;
  }

  private static void layoutOfAutoLeft(MindMapNode center){
        center.setDeep(1);
        getDeepOfAutoLeft(center);
        model = maxDeep;
       searchLeafOfLeftAutoLayout(center);
       currentLeaf = null;
       model = 0;
       maxDeep = 1;
  }

  private static void searchLeafOfRightLayout(MindMapNode node){
      ArrayList<MindMapNode> nodes = node.getChildNode();
      for(MindMapNode n : nodes){
          if(n.getDeep()==maxDeep){
              if(n.getChildNode().size()==0) {
                  setLeafPositionOfRightLayout(n);
              }else{
                  maxDeep++;
              }
          }
          searchLeafOfRightLayout(n);
      }
      if(node.getChildNode().size()!=0) {
          node.setLayoutX(node.getDeep() * 150);
          node.setLayoutY(nodes.get(nodes.size() / 2).getY());
          maxDeep--;
      }
  }

  private static void searchLeafOfRightAutoLayout(MindMapNode node){
        if(node.isLeft()){
            return;
        }
      ArrayList<MindMapNode> nodes = node.getChildNode();
      for(MindMapNode n : nodes){
          if(n.getDeep()==maxDeep&&!n.isLeft()){
              if(n.getChildNode().size()==0) {
                  setLeafPositionOfRightLayout(n);
              }else{
                  maxDeep++;
              }
          }
          searchLeafOfRightAutoLayout(n);
      }
      if(node.getChildNode().size()!=0&&!node.isCenter()) {
          node.setLayoutX(node.getDeep() * 150+xOfCenter);
          node.setLayoutY(nodes.get(nodes.size() / 2).getY());
          maxDeep--;
      }
  }

  private static void searchLeafOfLeftLayout(MindMapNode node){
      ArrayList<MindMapNode> nodes = node.getChildNode();
      for(MindMapNode n : nodes){
          if(n.getDeep()==maxDeep){
              if(n.getChildNode().size()==0) {
                  setLeafPositionOfLeftLayout(n);
              }else{
                  maxDeep++;
              }
          }
          searchLeafOfLeftLayout(n);
      }
      if(node.getChildNode().size()!=0) {
              node.setLayoutX(model * 150-node.getDeep()*150+200);
              node.setLayoutY(nodes.get(nodes.size() / 2).getY());
          maxDeep--;
      }
  }

  private static void searchLeafOfLeftAutoLayout(MindMapNode node){
        if(!node.isLeft()){
            return;
        }
      ArrayList<MindMapNode> nodes = node.getChildNode();
      for(MindMapNode n : nodes){
          if(n.getDeep()==maxDeep&&n.isLeft()){
              if(n.getChildNode().size()==0) {
                  setLeafPositionOfLeftLayout(n);
              }else{
                  maxDeep++;
              }
          }
          searchLeafOfLeftAutoLayout(n);
      }
      if(node.getChildNode().size()!=0) {
          if(node.isCenter()){
              node.setLayoutX(model * 150 - node.getDeep() * 150 + 260);
              node.setLayoutY(dpNodes.get(dpNodes.size()-1).get((dpNodes.get(dpNodes.size()-1).size())/2).getY());
          }else {
              node.setLayoutX(model * 150 - node.getDeep() * 150 + 200);
              node.setLayoutY(nodes.get(nodes.size() / 2).getY());
          }
          maxDeep--;
      }
  }


  private static void setLeafPositionOfRightLayout(MindMapNode node){
      if(currentLeaf==null){
          node.setLayoutX(node.getDeep()*150+xOfCenter);
          node.setLayoutY(200);
          currentLeaf = node;
      }else{
          node.setLayoutX(node.getDeep()*150+xOfCenter);
          node.setLayoutY(currentLeaf.getY()+60);
          currentLeaf = node;
      }
  }

  private static void setLeafPositionOfLeftLayout(MindMapNode node){
      if(currentLeaf==null){
          node.setLayoutX(model * 150-node.getDeep()*150+200);
          node.setLayoutY(200);
          currentLeaf = node;
      }else{
          node.setLayoutX(model * 150-node.getDeep()*150+200);
          node.setLayoutY(currentLeaf.getY()+60);
          currentLeaf = node;
      }
  }


  private static void getDeep(MindMapNode node){
      ArrayList<MindMapNode> nodes = node.getChildNode();
      for(MindMapNode n : nodes){
            n.setDeep(node.getDeep()+1);
            maxDeep = Math.max(n.getDeep(),maxDeep);
            getDeep(n);
      }
  }

  private static void getDeepOfAutoLeft(MindMapNode node){
        if(!node.isLeft()){
            return;
        }
      ArrayList<MindMapNode> nodes = node.getChildNode();
      for(MindMapNode n : nodes){
          n.setDeep(node.getDeep()+1);
          maxDeep = Math.max(n.getDeep(),maxDeep);
          getDeepOfAutoLeft(n);
      }
  }

  private static void getDeepOfAutoRight(MindMapNode node){
      if(node.isLeft()){
          return;
      }
      ArrayList<MindMapNode> nodes = node.getChildNode();
      for(MindMapNode n : nodes){
          n.setDeep(node.getDeep()+1);
          maxDeep = Math.max(n.getDeep(),maxDeep);
          getDeepOfAutoRight(n);
      }
  }

  private static int getHigh(MindMapNode node){
      ArrayList<MindMapNode> nodes = node.getChildNode();
      for(MindMapNode n : nodes){
          node.addHigh(getHigh(n));
      }
      if(node.getChildNode().size()==0){
          return 1;
      }else {
          return node.getHigh();
      }
  }

  private static void reOrder(MindMapNode center){
      ArrayList<MindMapNode> nodes = center.getChildNode();
      ArrayList<MindMapNode> nodesWithoutChild = new ArrayList<>();
      for(int i = 0;i<nodes.size();i++){
          MindMapNode t = nodes.get(i);
          if(t.getChildNode().size()==0){
              nodesWithoutChild.add(nodes.remove(i));
              i--;
          }
          reOrder(t);
      }
      nodes.addAll(nodesWithoutChild);
  }

  private static boolean getClosestValue(int next,int current,int index){
      if(next>current){
          dp[index] = next;
          return true;
      }
      return false;
  }

  private static void initdpNodes(int size){
      while ((size--)!=0){
          dpNodes.add(new ArrayList<MindMapNode>());
      }
  }

  private static void setStatusOfdpNodes(){
        ArrayList<MindMapNode> list = dpNodes.get(dpNodes.size()-1);
        for(MindMapNode n : list){
            setStatus(n);
        }
  }

  private static void setStatus(MindMapNode node){
        node.setLeft(true);
        ArrayList<MindMapNode> list = node.getChildNode();
        for(MindMapNode n : list){
            setStatus(n);
        }
  }
}